﻿using System.Collections.Generic;
using System.Linq;
using JSC.Portal.Entities;
using JSC.Core;
using JSC.Core.Data;
using JSC.Portal.Services;
using System.IO;
using System.Text.RegularExpressions;

namespace JSC.Portal.Data
{
    public class PortalPageService : CommonDao<PortalPage>, IPortalPageService
    {
        #region IPageService Members

        public IList<ModuleType> GetAllModuleTypesInUse()
        {
            string hql = "select s.ModuleType from Section s where s.ModuleType is not null group by s.ModuleType.Id";
            IDao<ModuleType> mtDao = new CommonDao<ModuleType>();
            return mtDao.GetByQuery(hql, null);
        }

        public PortalPage GetPortalPageByName(string name)
        {
            string hql = "from PortalPage pp where lower(pp.Name) = :name";
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("name", name.ToLowerInvariant());
            return GetUniqueByQuery(hql, parameters);
        }

        public PortalPage GetPortalPageByFriendlyURL(string siteAddress, string language, string url)
        {
#if NHibernateLINQ
            return (from pp in Dao.Linq() where pp.FriendlyURL == url select pp).Single();
#else
            string hql = "from PortalPage pp where lower(pp.Language) = :language and lower(pp.FriendlyURL) = :friendlyUrl and lower(pp.PortalSite.Url) = :site";
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("language", language.ToLowerInvariant());
            parameters.Add("friendlyUrl", url.ToLowerInvariant());
            parameters.Add("site", siteAddress.ToLowerInvariant());
            return GetUniqueByQuery(hql, parameters);
#endif
         }

        public PortalPage GetRootPortalPage(string siteAddress, string language)
        {
#if NHibernateLINQ
            return (from pp in Dao.Linq<PortalPage>() where pp.Parent == null select pp).Single();
#else            
            string hql = "from PortalPage pp where pp.Parent is null and lower(pp.Language) = :language and lower(pp.PortalSite.Url) = :site";
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("language", language.ToLowerInvariant());
            parameters.Add("site", siteAddress.ToLowerInvariant());
            return GetUniqueByQuery(hql, parameters);
#endif        
        }

        public void PropagatePermissionsToChildPages(PortalPage parentPage, bool propagateToSections)
        {
            foreach (PortalPage childPage in parentPage.ChildPages)
            {
                childPage.PagePermissions.Clear();
                SetPermissions(parentPage, childPage);
                if (propagateToSections)
                {
                    PropagatePermissionsToSections(childPage);
                }
                PropagatePermissionsToChildPages(childPage, propagateToSections);
            }
        }

        public void PropagatePermissionsToSections(PortalPage portalPage)
        {
            foreach (Section section in portalPage.Sections)
            {
                section.SectionPermissions.Clear();
                foreach (PortalPagePermission ppp in portalPage.PagePermissions)
                {
                    SectionPermission sp = new SectionPermission();
                    sp.Section = section;
                    sp.Role = ppp.Role;
                    sp.ViewAllowed = ppp.ViewAllowed;
                    sp.EditAllowed = ppp.EditAllowed;
                    section.SectionPermissions.Add(sp);
                }
            }
        }

        public void MovePortalPage(PortalPage portalPage, MoveAction moveAction)
        {
            /// Vreemd genoeg doet onderstaande Linq query het niet
            /// from pp in _pageDao.Linq() where pp.Parent == null select pp).ToList();
            IList<PortalPage> rootPages = GetByQuery("from PortalPage pp where pp.Parent is null", null);
            switch (moveAction)
            {   
                case MoveAction.MoveUp:
                    MoveUp(portalPage, rootPages);
                    break;
                case MoveAction.MoveDown:
                    MoveDown(portalPage, rootPages);
                    break;
                case MoveAction.MoveLeft:
                    MoveLeft(portalPage, rootPages);
                    break;
                case MoveAction.MoveRight:
                    MoveRight(portalPage, rootPages);
                    break;
                default:
                    break;
            }
        }

        public PortalPage GetNewPortalPageBySiteId(int siteId)
        {
            IPortalSiteService pssSvc = FacadeFactory.CreateFacade<IPortalSiteService>();
            PortalSite portalSite = pssSvc.GetById(siteId, false);
            PortalPage pp = new PortalPage();
            pp.PortalSite = portalSite;
            pp.Language = portalSite.DefaultLanguage;
            pp.Template = portalSite.DefaultTemplate;
            pp.Position = 0;

            return pp;
        }

        public PortalPage GetNewChildPage(int parentId)
        {
            PortalPage parent = GetById(parentId, false);
            PortalPage pp = new PortalPage();
            pp.Parent = parent;
            pp.PortalSite = parent.PortalSite;
            pp.Language = parent.PortalSite.DefaultLanguage;
            pp.Template = parent.PortalSite.DefaultTemplate;
            SetPermissions(parent, pp);

            return pp;
        }

        public IList<string> GetTemplatePlaceholders(string path)
        {
            IList<string> result = new List<string>();
            using (TextReader tr = File.OpenText(path))
            {
                string input = tr.ReadToEnd();
                MatchCollection matches = Regex.Matches(input, @"(Html.RenderModuleControls\("")(.*)(\""\);)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);
                foreach (Match m in matches)
                {
                    result.Add(m.Groups[2].Value);
                }
                tr.Close();
            }
            return result;
        }

        public IList<Language> GetAvailableLanguages(PortalPage portalPage)
        {
            IList<Language> allLanguages = Language.GetLanguages();
            if (portalPage.Parent != null)
                return allLanguages;

            if (portalPage.IsNew)
            {
                IEnumerable<PortalPage> rootPages = portalPage.PortalSite.PortalPages.Where(x => x.Parent == null);
                var result = from l in allLanguages
                             where !(from p in rootPages select p.Language).Contains(l.Culture)
                             select l;
                return result.ToList();
            }
            else
            {
                return allLanguages.Where(x => x.Culture == portalPage.Language).ToList();
            }
        }

        public IList<PortalPage> GetLanguageRootPagesBySite(PortalSite portalSite)
        {

            return portalSite.PortalPages.Where(p => p.Parent == null).ToList();
        }

        #endregion

        private void MoveUp(PortalPage portalPage, IList<PortalPage> rootPages)
        {
            if (portalPage.Position > 0)
            {
                // HACK: Assume that the node indexes are the same as the value of the positions.
                portalPage.Position--;
                IList<PortalPage> parentChildPages = (portalPage.Parent == null ? rootPages : portalPage.Parent.ChildPages);
                parentChildPages[portalPage.Position].Position++;
                parentChildPages.Remove(portalPage);
                parentChildPages.Insert(portalPage.Position, portalPage);
            }
        }

        private void MoveDown(PortalPage portalPage, IList<PortalPage> rootPages)
        {
            if (portalPage.Position < portalPage.Parent.ChildPages.Count - 1)
            {
                // HACK: Assume that the node indexes are the same as the value of the positions.
                portalPage.Position++;
                IList<PortalPage> parentChildPages = (portalPage.Parent == null ? rootPages : portalPage.Parent.ChildPages);
                parentChildPages[portalPage.Position].Position--;
                parentChildPages.Remove(portalPage);
                parentChildPages.Insert(portalPage.Position, portalPage);
            }
        }

        private void MoveLeft(PortalPage portalPage, IList<PortalPage> rootPages)
        {
            int newPosition = portalPage.Parent.Position + 1;
            if (portalPage.Parent.Level == 0)
            {
                for (int i = newPosition; i < rootPages.Count; i++)
                {
                    PortalPage pageAlsoToBeMoved = rootPages[i];
                    pageAlsoToBeMoved.Position++;
                }
            }
            else
            {
                for (int i = newPosition; i < portalPage.Parent.Parent.ChildPages.Count; i++)
                {
                    PortalPage pageAlsoToBeMoved = portalPage.Parent.Parent.ChildPages[i];
                    pageAlsoToBeMoved.Position++;
                }
            }
            portalPage.Parent.ChildPages.Remove(portalPage);
            ReOrderNodePositions(portalPage.Parent.ChildPages, portalPage.Position);
            portalPage.Parent = portalPage.Parent.Parent;
            if (portalPage.Parent != null)
            {
                portalPage.Parent.ChildPages.Add(portalPage);
            }
            portalPage.Position = newPosition;
        }

        private void MoveRight(PortalPage portalPage, IList<PortalPage> rootPages)
        {
            if (portalPage.Position > 0)
            {
                PortalPage previousSibling;
                if (portalPage.Parent != null)
                {
                    previousSibling = portalPage.Parent.ChildPages[portalPage.Position - 1];
                    portalPage.Parent.ChildPages.Remove(portalPage);
                    ReOrderNodePositions(portalPage.Parent.ChildPages, portalPage.Position);
                }
                else
                {
                    previousSibling = rootPages[portalPage.Position - 1];
                    ReOrderNodePositions(rootPages, portalPage.Position);
                }

                portalPage.Position = previousSibling.ChildPages.Count;
                previousSibling.ChildPages.Add(portalPage);
                portalPage.Parent = previousSibling;
            }
        }

        private void ReOrderNodePositions(IList<PortalPage> pages, int gapPosition)
        {
            foreach (PortalPage page in pages)
            {
                if (page.Position > gapPosition)
                {
                    page.Position--;
                }
            }
        }

        private void SetPermissions(PortalPage parentPage, PortalPage childPage)
        {
            foreach (PortalPagePermission ppp in parentPage.PagePermissions)
            {
                PortalPagePermission childPagePermission = new PortalPagePermission();
                childPagePermission.Page = childPage;
                childPagePermission.Role = ppp.Role;
                childPagePermission.ViewAllowed = ppp.ViewAllowed;
                childPagePermission.EditAllowed = ppp.EditAllowed;
                childPage.PagePermissions.Add(childPagePermission);
            }
        }
    }
}
